package com.wangc.commons.exception.handler;

import com.fasterxml.jackson.databind.exc.InvalidFormatException;
import com.wangc.commons.exception.BusinessException;
import com.wangc.commons.exception.CheckedException;
import com.wangc.commons.result.RespResult;
import com.wangc.commons.utils.CommonsUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.converter.HttpMessageNotReadableException;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import org.springframework.web.util.ContentCachingRequestWrapper;

import javax.servlet.http.HttpServletRequest;
import javax.validation.ConstraintViolationException;
import java.nio.charset.StandardCharsets;
import java.util.StringJoiner;

/**
 * 异常全局捕捉
 *
 * @author LoongChow
 * @date 2018/10/26
 */
@Slf4j
@RestControllerAdvice
public class CommonExceptionHandler {

    /**
     * hibernat validation action参数为java类时的校验拦截
     *
     * @param exs MethodArgumentNotValidException
     * @return ResponseEntity
     */
    @ExceptionHandler(value = MethodArgumentNotValidException.class)
    @ResponseBody
    public RespResult handle(MethodArgumentNotValidException exs, HttpServletRequest request) {
        printException(exs, request);
        StringJoiner joiner = new StringJoiner(",");
        exs.getBindingResult().getFieldErrors()
                .forEach(p -> joiner.add(p.getDefaultMessage()));
        return RespResult.error(joiner.toString());
    }

    /**
     * hibernate validation action参数为java常用数据类型时的校验拦截
     *
     * @param exs ConstraintViolationException
     * @return ResponseEntity
     */
    @ExceptionHandler(value = ConstraintViolationException.class)
    @ResponseBody
    public RespResult handle(ConstraintViolationException exs, HttpServletRequest request) {
        printException(exs, request);
        StringJoiner joiner = new StringJoiner(",");
        exs.getConstraintViolations().forEach(p -> joiner.add(p.getMessage()));
        return RespResult.error(joiner.toString());
    }

    /**
     * 自定义非运行时异常拦截
     *
     * @param exs CheckedException
     * @return ResponseEntity
     */
    @ExceptionHandler(value = CheckedException.class)
    @ResponseBody
    public RespResult handle(CheckedException exs, HttpServletRequest request) {
        printException(exs, request);
        return RespResult.error(exs.getMessage());
    }

    /**
     * 自定义业务异常拦截
     *
     * @param exs BusinessException
     * @return ResponseEntity
     */
    @ExceptionHandler(value = BusinessException.class)
    @ResponseBody
    public RespResult handle(BusinessException exs, HttpServletRequest request) {
        printException(exs, request);
        return RespResult.error(exs.getReturnCode(), exs.getMessage());
    }

    @ExceptionHandler(value = { HttpMessageNotReadableException.class, InvalidFormatException.class })
    @ResponseBody
    public RespResult parseErr(HttpMessageNotReadableException exs, HttpServletRequest request) {
        printException(exs, request);
        return RespResult.error(50000, "参数解析失败!");
    }

    /**
     * 基于exception的异常拦截
     *
     * @param exs Exception
     * @return ResponseEntity
     */
    @ExceptionHandler(value = Exception.class)
    @ResponseBody
    public RespResult handle(Exception exs, HttpServletRequest request) {
        printException(exs, request);
        return RespResult.error(50000, "系统异常!");
    }

    //    /**
    //     * 异常打印
    //     *
    //     * @param e Exception
    //     */
    //    public static void printException(Exception e) {
    //        log.error("exception handle print exception", e);
    //    }

    /**
     * 打印日志
     *
     * @param ex
     * @param request
     */
    private void printException(Exception ex, HttpServletRequest request) {
        try {
            ContentCachingRequestWrapper re = new ContentCachingRequestWrapper(request);;
            StringBuilder stringBuilder = new StringBuilder();
            stringBuilder.append("mark_end|uri=").append(re.getRequestURI()).append("|request=").append(new String(re.getContentAsByteArray(), StandardCharsets.UTF_8)).append("|ip=").append(CommonsUtil.getRemoteAddress(re)).append("|");
            log.info("afterCompletion url:{}, spendTime: {}", re.getRequestURI(),
                    (System.currentTimeMillis() - (Long) re.getAttribute("startTime")));
            stringBuilder.append(ex);
            log.error("{}", stringBuilder.toString());
            log.error("detailed error：", ex);
        } catch (Exception e) {
            log.error("mark_end|", ex);
        }
    }
}
